#pragma once
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
#include <string>
#include <memory>
#include <iostream>
#include <vector>
#include <errno.h>
#include <sys/wait.h>

using namespace std;


int default_perm = 0666;
string path(".");
int PROJ_ID = 0x7866;

#define GET_SEM IPC_CREAT
#define CREATE_SEM (IPC_CREAT | IPC_EXCL)

//基于创造者模式进行结构的创建

//具体的操作 -> 产品做
class Semaphore{
private:
    void PV(int who, int opr){
        //因为当前信号量集二元，所以内部只有一个信号量，编号为0
        struct sembuf _sem_buf;
        _sem_buf.sem_flg = 0;
        _sem_buf.sem_op = opr;
        _sem_buf.sem_num = who;

        int semop_rd = semop(_semid, &_sem_buf, 1);
        if(semop_rd < 0){
            cerr << "semop error!" << endl;
            exit(errno);
        }
    }
public:
    Semaphore(int semid, int flag)
        :_semid(semid), _flag(flag)
    {}

    //用户决定申请编号为who的信号量
    void P(int who){
        PV(who, -1);
    }

    void V(int who){
        PV(who, 1);
    }

    ~Semaphore(){
        if(_flag == GET_SEM) return;
        
        cout << "it's going to remove sem!" << endl;
        int rm_sem_id = semctl(_semid, 0, IPC_RMID);
        if(rm_sem_id < 0){
            cerr << "remove sem error!" << endl;
            exit(errno);
        }
        else cout << "remove sem success!" << endl;
    }
private:
    int _semid;
    int _flag;
};


//只写出建造的方法，让真正的创建者Bulid进行虚函数重写，实现指定创建方式
class Bulid_Opeator{
public:
    virtual void GetKey() = 0;
    virtual void GetPerm(int perm) = 0;
    virtual void GetNsems(int nsems) = 0;
    virtual void GetFlag(int flag) = 0;
    virtual void GetNsemsVal(vector<int> initval_vector) = 0; 
    // 将信号量集中每个编号(从0开始)的值放在这个vector中
    virtual void Bulid(int flag) = 0;
    virtual void Init() = 0;
    
};


//继承建造方法 完成产品创建的各类方法
class SemaphoreBulider : public Bulid_Opeator{
public:
    SemaphoreBulider() = default;

    virtual void GetKey() override{
        _key = ftok(path.c_str(), PROJ_ID);
        if(_key < 0) {
            cerr << "ftok error!" << endl;
            exit(errno);
        }
        else cout << "ftok success! key : " << _key << endl;
    }

    virtual void GetPerm(int perm) override{
        _perm = perm;
    }

    virtual void GetNsems(int nsems) override{
        _nsems = nsems;
    }

    virtual void GetFlag(int flag) override{
        _flag = flag;
    }

    virtual void GetNsemsVal(vector<int> initval_vector) override{
        _initval_vector = initval_vector;
    }

    virtual void Bulid(int flg) override{
        _semid = semget(_key, _nsems, flg);
        if(_semid < 0){
            cerr << "semget error!" << endl;
            exit(errno);
        }
        else cout << "semget success!" << endl;
    }

    virtual void Init() override{
        if(_flag == GET_SEM) return;
        // 此时就不是单纯的对信号集中某一个编号的信号量进行初始化，而是对所有的进行初始化
        // 初始化的值就放在了_initval_vector数组中

        union semun {
            int              val;    /* Value for SETVAL val > 0*/
            struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
            unsigned short  *array;  /* Array for GETALL, SETALL */
            struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                           (Linux-specific) */
        }un;

        for(int i = 0; i < _nsems; ++i){
            un.val = _initval_vector[i];
            int semctl_rd = semctl(_semid, i, SETVAL, un);
            if(semctl_rd < 0){
                cerr << "sem init(SETVAL) error!" << endl;
                exit(errno);
            }
            else cout << "sem init(SETVAL) success!" << endl;
        }
    }

    // 获取到一个指向信号量集的智能指针 直接就可以使用
    shared_ptr<Semaphore> outer_get_semaphorePtr(){
        return make_shared<Semaphore>(_semid, _flag);
    }

private:
    key_t _key;
    int _perm;
    int _nsems;
    int _semid;
    int _flag;
    vector<int> _initval_vector;
};


//指挥官 -> 指挥builder造出产品
class Semaphore_Director{
public:
    Semaphore_Director()
    {}
    ~Semaphore_Director(){}

    void Organize_Bulid
    (SemaphoreBulider& sem_bulider, int flag, int perm = 0666, int nsems = 1, vector<int> initval = {1}){
        sem_bulider.GetKey();
        // 一定要先设定权限值
        sem_bulider.GetPerm(perm);
        sem_bulider.GetNsems(nsems);
        sem_bulider.GetFlag(flag);
        sem_bulider.GetNsemsVal(initval);
        sem_bulider.Bulid(flag | perm);
        sem_bulider.Init();
    }

    shared_ptr<Semaphore> Get_SemaphorePtr(SemaphoreBulider& sem_bulider){
        return sem_bulider.outer_get_semaphorePtr();
    }
    
private:
};